{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Redis的发布订阅模式\n",
    "\n",
    "![](https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/2019-03-03-16-52-34.png)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 发布订阅模式的基本语法\n",
    "\n",
    "\n",
    "### 发布订阅模式是什么\n",
    "\n",
    "发布订阅模式就像是收音机一样，发布的人不关心有没有人收听，也不关心有多少人收听。他只管发布。接收的人需要订阅这个频道才能收到内容，在订阅之前的内容，接收人都看不到。在他订阅之后，发布者发布的所有信息它都能收听到，直到接收人主动退订为止。发布者发布的所有信息，能同时被所有订阅了这个频道的人接收到。被发布的信息不会储存，发布以后无论有没有人接收，都会直接销毁。\n",
    "\n",
    "发布订阅模式模式可以用在聊天室，或者其他需要使用到“广播”的场景。\n",
    "\n",
    "### 发布订阅模式的基本语法\n",
    "\n",
    "#### 发布消息\n",
    "\n",
    "```python\n",
    "client.publish('频道名字', '被广播出来的数据')\n",
    "```\n",
    "\n",
    "#### 接收数据\n",
    "\n",
    "```python\n",
    "\n",
    "# 允许出现订阅、取消订阅的确认信息\n",
    "channel = client.pubsub()\n",
    "\n",
    "# 自动过滤订阅、取消订阅的确认信息\n",
    "channel = client.pubsub(ignore_subscribe_messages=True)\n",
    "\n",
    "\n",
    "# 根据完整的频道名字订阅\n",
    "channel.subscribe('第一个频道', '第二个频道')\n",
    "\n",
    "# 使用通配符订阅频道\n",
    "channel.psubscribe('闲话*', '情感*')\n",
    "\n",
    "\n",
    "# 非阻塞式写法\n",
    "channel.get_message()   # 接收第一条信息\n",
    "channel.get_message()  # 接收第二条信息\n",
    "\n",
    "# 阻塞式写法，如果没有消息，程序会卡在这里，直到有消息进来\n",
    "for message in p.listen():\n",
    "    print(message)\n",
    "```\n",
    "\n",
    "#### 注册回调函数\n",
    "\n",
    "```python\n",
    "def check_at_me(message):\n",
    "    msg = message['data']\n",
    "    if 'kingname' in msg:\n",
    "        print(f'有人在@我，消息内容为：{msg}')\n",
    "channel = client.pubsub()\n",
    "channel.subscribe(频道名=回调函数)\n",
    "channel.get_message()\n",
    "channel.get_message()\n",
    "```\n",
    "\n",
    "#### 取消订阅\n",
    "\n",
    "```python\n",
    "\n",
    "# 取消全部频道\n",
    "channel.unsubscribe()\n",
    "\n",
    "# 根据完整名称取消特定的订阅\n",
    "channel.unsubscribe('第一个频道', '第二个频道')\n",
    "\n",
    "# 根据通配符取消特定的订阅\n",
    "channel.punsubscribe('闲话*')\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "## 初始化环境\n",
    "\n",
    "import redis\n",
    "\n",
    "client = redis.Redis()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 发布消息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 注意，此时没有任何人订阅“音乐之声”这个频道，所以没有人能收听到这一条消息\n",
    "client.publish('音乐之声', '第一首歌：纸短情长')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 订阅频道并接收消息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "music = client.pubsub()\n",
    "music.subscribe('音乐之声')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'channel': b'\\xe9\\x9f\\xb3\\xe4\\xb9\\x90\\xe4\\xb9\\x8b\\xe5\\xa3\\xb0',\n",
       " 'data': 1,\n",
       " 'pattern': None,\n",
       " 'type': 'subscribe'}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 刚刚订阅完成频道，获得的第一条信息是订阅确认信息\n",
    "music.get_message()  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None\n"
     ]
    }
   ],
   "source": [
    "# 此时如果没有新的消息发布，那么获得的信息为None\n",
    "\n",
    "print(music.get_message())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "client.publish('音乐之声', '第二首歌：核爆神曲')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "信息的完整内容为： {'type': 'message', 'pattern': None, 'channel': b'\\xe9\\x9f\\xb3\\xe4\\xb9\\x90\\xe4\\xb9\\x8b\\xe5\\xa3\\xb0', 'data': b'\\xe7\\xac\\xac\\xe4\\xba\\x8c\\xe9\\xa6\\x96\\xe6\\xad\\x8c\\xef\\xbc\\x9a\\xe6\\xa0\\xb8\\xe7\\x88\\x86\\xe7\\xa5\\x9e\\xe6\\x9b\\xb2'}\n",
      "信息内容: 第二首歌：核爆神曲\n"
     ]
    }
   ],
   "source": [
    "# 此时这个频道里面已经有消息了，所以可以获取消息了\n",
    "\n",
    "message = music.get_message()\n",
    "print('信息的完整内容为：', message)\n",
    "print(f'信息内容: {message[\"data\"].decode()}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'第二首歌：核爆神曲'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "message['data'].decode()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "## 订阅多个频道\n",
    "\n",
    "chat = client.pubsub(ignore_subscribe_messages=True)\n",
    "chat.subscribe('鬼畜专区', '二次元专区')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "client.publish('鬼畜专区', 'Are you OK?')\n",
    "client.publish('二次元专区', '海贼王更新了！！！')\n",
    "client.publish('二次元专区', '柯南大结局了！！！')\n",
    "client.publish('鬼畜专区', '金坷垃')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None\n"
     ]
    }
   ],
   "source": [
    "# 忽略订阅消息以后，返回的前两条信息为None，这是第一条\n",
    "message = chat.get_message()\n",
    "print(message)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None\n"
     ]
    }
   ],
   "source": [
    "# 忽略订阅消息以后，返回的前两条信息为None，这是第二条\n",
    "message = chat.get_message()\n",
    "print(message)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Are you OK?\n"
     ]
    }
   ],
   "source": [
    "message = chat.get_message()\n",
    "print(message['data'].decode())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "海贼王更新了！！！\n"
     ]
    }
   ],
   "source": [
    "message = chat.get_message()\n",
    "print(message['data'].decode())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "柯南大结局了！！！\n"
     ]
    }
   ],
   "source": [
    "message = chat.get_message()\n",
    "print(message['data'].decode())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "金坷垃\n"
     ]
    }
   ],
   "source": [
    "message = chat.get_message()\n",
    "print(message['data'].decode())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用通配符订阅频道\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "chat.psubscribe('闲话*')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "client.publish('闲话1群', '大家随便聊啊')\n",
    "client.publish('闲话2群', '来来来满上满上')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None\n"
     ]
    }
   ],
   "source": [
    "# 第一次返回为None\n",
    "message = chat.get_message()\n",
    "print(message)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'type': 'pmessage', 'pattern': b'\\xe9\\x97\\xb2\\xe8\\xaf\\x9d*', 'channel': b'\\xe9\\x97\\xb2\\xe8\\xaf\\x9d1\\xe7\\xbe\\xa4', 'data': b'\\xe5\\xa4\\xa7\\xe5\\xae\\xb6\\xe9\\x9a\\x8f\\xe4\\xbe\\xbf\\xe8\\x81\\x8a\\xe5\\x95\\x8a'}\n",
      "大家随便聊啊\n"
     ]
    }
   ],
   "source": [
    "message = chat.get_message()\n",
    "print(message)\n",
    "print(message['data'].decode())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'type': 'pmessage', 'pattern': b'\\xe9\\x97\\xb2\\xe8\\xaf\\x9d*', 'channel': b'\\xe9\\x97\\xb2\\xe8\\xaf\\x9d2\\xe7\\xbe\\xa4', 'data': b'\\xe6\\x9d\\xa5\\xe6\\x9d\\xa5\\xe6\\x9d\\xa5\\xe6\\xbb\\xa1\\xe4\\xb8\\x8a\\xe6\\xbb\\xa1\\xe4\\xb8\\x8a'}\n",
      "来来来满上满上\n"
     ]
    }
   ],
   "source": [
    "message = chat.get_message()\n",
    "print(message)\n",
    "print(message['data'].decode())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "client.publish('闲话1群', '今晚谁家吃饭啊')\n",
    "client.publish('闲话1群', '走我家去')\n",
    "client.publish('闲话1群', '好啊好啊')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "今晚谁家吃饭啊\n",
      "走我家去\n",
      "好啊好啊\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-26-4b83639063d2>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mmessage\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mchat\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlisten\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      2\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mmessage\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m         \u001b[0;32mcontinue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m     \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmessage\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'data'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/redis/client.py\u001b[0m in \u001b[0;36mlisten\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m   3121\u001b[0m         \u001b[0;34m\"Listen for messages on channels this client has been subscribed to\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   3122\u001b[0m         \u001b[0;32mwhile\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msubscribed\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 3123\u001b[0;31m             \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhandle_message\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparse_response\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mblock\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   3124\u001b[0m             \u001b[0;32mif\u001b[0m \u001b[0mresponse\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   3125\u001b[0m                 \u001b[0;32myield\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/redis/client.py\u001b[0m in \u001b[0;36mparse_response\u001b[0;34m(self, block, timeout)\u001b[0m\n\u001b[1;32m   3034\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mblock\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcan_read\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   3035\u001b[0m             \u001b[0;32mreturn\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 3036\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_execute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconnection\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_response\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   3037\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   3038\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0m_normalize_keys\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/redis/client.py\u001b[0m in \u001b[0;36m_execute\u001b[0;34m(self, connection, command, *args)\u001b[0m\n\u001b[1;32m   3011\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0m_execute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconnection\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcommand\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   3012\u001b[0m         \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 3013\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mcommand\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   3014\u001b[0m         \u001b[0;32mexcept\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mConnectionError\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTimeoutError\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   3015\u001b[0m             \u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdisconnect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/redis/connection.py\u001b[0m in \u001b[0;36mread_response\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    635\u001b[0m         \u001b[0;34m\"Read the response from a previously sent command\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    636\u001b[0m         \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 637\u001b[0;31m             \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_parser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_response\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    638\u001b[0m         \u001b[0;32mexcept\u001b[0m\u001b[0;34m:\u001b[0m  \u001b[0;31m# noqa: E722\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    639\u001b[0m             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdisconnect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/redis/connection.py\u001b[0m in \u001b[0;36mread_response\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    288\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    289\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mread_response\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 290\u001b[0;31m         \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_buffer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreadline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    291\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    292\u001b[0m             \u001b[0;32mraise\u001b[0m \u001b[0mConnectionError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mSERVER_CLOSED_CONNECTION_ERROR\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/redis/connection.py\u001b[0m in \u001b[0;36mreadline\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    222\u001b[0m         \u001b[0;32mwhile\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mendswith\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mSYM_CRLF\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    223\u001b[0m             \u001b[0;31m# there's more data in the socket that we need\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 224\u001b[0;31m             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_read_from_socket\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    225\u001b[0m             \u001b[0mbuf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mseek\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbytes_read\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    226\u001b[0m             \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbuf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreadline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/redis/connection.py\u001b[0m in \u001b[0;36m_read_from_socket\u001b[0;34m(self, length)\u001b[0m\n\u001b[1;32m    180\u001b[0m         \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    181\u001b[0m             \u001b[0;32mwhile\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 182\u001b[0;31m                 \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrecv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_sock\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msocket_read_size\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    183\u001b[0m                 \u001b[0;31m# an empty string indicates the server shutdown the socket\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    184\u001b[0m                 \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbytes\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/lib/python3.6/site-packages/redis/_compat.py\u001b[0m in \u001b[0;36mrecv\u001b[0;34m(sock, *args, **kwargs)\u001b[0m\n\u001b[1;32m     56\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m  \u001b[0;31m# Python 3.5 and above automatically retry EINTR\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     57\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mrecv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msock\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 58\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0msock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrecv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     59\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     60\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mrecv_into\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msock\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
     ]
    }
   ],
   "source": [
    "for message in chat.listen():\n",
    "    if not message:\n",
    "        continue\n",
    "    print(message['data'].decode())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 注册回调函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def check_at_me(message):\n",
    "    msg = message['data'].decode()\n",
    "    if 'kingname' in msg:\n",
    "        print(f'有人在@我，消息内容为：{msg}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "emotion = client.pubsub(ignore_subscribe_messages=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "emotion.subscribe(情感1群=check_at_me)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "client.publish('情感1群', '大家好，给大家介绍一下这是我的女朋友')\n",
    "client.publish('情感1群', '群主@kingname, 快吧这个撒狗粮的踢了')\n",
    "client.publish('情感1群', '已踢')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "emotion.get_message()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "emotion.get_message()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "有人在@我，消息内容为：群主@kingname, 快吧这个撒狗粮的踢了\n"
     ]
    }
   ],
   "source": [
    "emotion.get_message()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 取消订阅\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "emotion.unsubscribe()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "emotion.get_message()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "chat.punsubscribe('闲话*')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 功能测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "client.publish('闲话1群', '行恩和')\n",
    "client.publish('闲话1群', '嘿嘿嘿')\n",
    "client.publish('闲话1群', '测试测试')\n",
    "client.publish('闲话2群', '啊啊')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![读者交流QQ群](https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/2019-02-16-09-59-56.png)\n",
    "![微信公众号](https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/wechatplatform.jpg)\n",
    "![](https://kingname-1257411235.cos.ap-chengdu.myqcloud.com/2019-03-03-20-47-47.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
